using System;
using Sedna.Core.Support;

namespace Sedna.Domain
{
    /// <summary>
    /// read-only value object class
    /// 
    /// to map this class to the DB with NHibernate the <see cref="Sedna.Core.Persistence.HoldStatusType"/> is used
    /// 
    /// 
    /// There are two types of hold statuses: system and user.
    /// 
    /// System hold statuses - auxiliary statuses that are assigned by system operations (e.i. transactions) and
    ///                        can't be relieved by users. There are a set of predefined system hold statuses.
    ///                        All such statuses are negative.  Only system operations can reset thier statuses.
    ///                        Separatly transactional hold statuses can be extracted as a subset of system hold statuses.
    /// 
    /// User hold statuses - statuses that are assigned by the users. Users can define thier own hold statuses.
    ///                      All such statuses are positive. Only users can reset their statuses.
    /// 
    /// Zero hold status - free from hold.
    /// 
    /// Hold statuses can't be reassigned. To assign a new hold status it is necessary to reset a previous one.
    /// 
    /// </summary>
    [Serializable]
    public class HoldStatus : IComparable
    {
        // Don't change values! They are used in views & stored procedures.
        
        private static int DEFAULT_USER_HOLD = 1;
        
        private static int NOT_HELD = 0;
        
        private static int ON_TX_RECEIVING = -1;
        private static int ON_TX_SHIPPING = -2;
        private static int ON_TX_OWNERSHIP_TRANSFER = -3;
        private static int ON_TX_RECEIPT_CREATE = -4;
        private static int ON_TX_RECEIPT_CREATE_AND_OWNERSHIP_TRANSFER = -5;
        private static int ON_TX_RECEIPT_REDEEM = -6;
        private static int ON_TX_RRECEIPT_REDEEM_AND_OWNERSHIP_TRANSFER = -7;
        
        private static int ON_INTER_WAREHOUSE_TRANSFER = -11;        

        
        private int status;

        # region Constructors

        private HoldStatus() { }

        private HoldStatus(int status)
        {
            this.status = status;
        }

        # endregion

        public int Value
        {
            get { return status; }
        }

        # region Checking Properties

        public bool IsHeld
        {
            get { return status != NOT_HELD; }
        }
        
        /// <summary>
        /// Defines if user hold status.
        /// Must be held.
        /// </summary>
        public bool IsUser
        {
            get
            {
                Assert.IsTrue(IsHeld, "[HoldStatus.IsUserHoldStatus] It isn't a hold status");

                return IsUserHoldStatus(status);
            }
        }
        
        /// <summary>
        /// Defines if hold status was set by user
        /// </summary>
        public bool IsHeldByUser
        {
            get { return IsUserHoldStatus(status); }
        }

        /// <summary>
        /// Defines if hold status was set by OwnerShipTransfer
        /// </summary>
        public bool IsHeldByWarehouseTransfer
        {
            get { return IsWarehouseTransferHoldStatus(status); }
        }
        /// <summary>
        /// Defines if system hold status.
        /// Must be held.
        /// </summary>
        public bool IsSystem
        {
            get
            {
                Assert.IsTrue(IsHeld, "[HoldStatus.IsSystemHoldStatus] It isn't a hold status");

                return IsSystemHoldStatus(status);
            }
        }

        /// <summary>
        /// Defines if hold status was set by system
        /// </summary>
        public bool IsHeldBySystem
        {
            get { return IsSystemHoldStatus(status); }
        }
        
        /// <summary>
        /// Defines if transactional hold status (a subset of system hold statuses)
        /// </summary>
        public bool IsTransactional
        {
            get
            {
                Assert.IsTrue(IsHeld, "[HoldStatus.IsTransactional] It isn't a hold status");

                return IsTransactionalHoldStatus(status);
            }
        }

        /// <summary>
        /// Defines if hold status was set by business transaction
        /// </summary>
        public bool IsHeldByTransaction
        {
            get { return IsTransactionalHoldStatus(status); }
        }
        
        public bool IsHeldByReceivingTx
        {
            get { return status == ON_TX_RECEIVING; }
        }

        public bool IsHeldByShippingTx
        {
            get { return status == ON_TX_SHIPPING; }
        }

        public bool IsHeldByOwnershipTransferTx
        {
            get { return status == ON_TX_OWNERSHIP_TRANSFER; }
        }

        public bool IsHeldByReceiptCreationTx
        {
            get { return status == ON_TX_RECEIPT_CREATE; }
        }

        public bool IsHeldByReceiptCreationAndOwnershipTransferTx
        {
            get { return status == ON_TX_RECEIPT_CREATE_AND_OWNERSHIP_TRANSFER; }
        }

        public bool IsHeldByReceiptRedemptionTx
        {
            get { return status == ON_TX_RECEIPT_REDEEM; }
        }

        public bool IsHeldByReceiptRedemptionAndOwnershipTransferTx
        {
            get { return status == ON_TX_RRECEIPT_REDEEM_AND_OWNERSHIP_TRANSFER; }
        }

        public bool IsHeldByInterWarehouseTransferOperation
        {
            get { return status == ON_INTER_WAREHOUSE_TRANSFER; }
        }

        # endregion

        /// <summary>
        /// Don't call this method directly.
        /// Use <see cref="Hold(int)"/> method or <see cref="Released"/> property.
        /// </summary>
        public static HoldStatus Set(int status)
        {
            return new HoldStatus(status);
        }

        /// <summary>
        /// Passing status must be one of held status.
        /// It's better to use <see cref="HoldByUser(int)"/> method
        /// or one of the predefined system hold statuses.
        /// </summary>
        public static HoldStatus Hold(int status)
        {
            Assert.IsTrue(status != NOT_HELD, "[HoldStatus.Held] Status must be non-zero");

            return new HoldStatus(status);
        }

        /// <summary>
        /// Passing status must be one of user held status.
        /// </summary>
        public static HoldStatus HoldByUser(int status)
        {
            Assert.IsTrue(IsUserHoldStatus(status), "[HoldStatus.HeldByUser] Status must be greater than zero");

            return new HoldStatus(status);
        }
        
        public static HoldStatus HoldByUser()
        {
            return new HoldStatus(DEFAULT_USER_HOLD);
        }

        /// <summary>
        /// It's better to use one of the predefined system hold statuses.
        /// </summary>
        public static HoldStatus HoldBySystem(int status)
        {
            Assert.IsTrue(IsSystemHoldStatus(status), "[HoldStatus.HeldBySystem] Status must be less than zero");

            return new HoldStatus(status);
        }

        /// <summary>
        /// Sets free from hold status
        /// </summary>
        public static HoldStatus Released
        {
            get { return new HoldStatus(NOT_HELD); }
        }

        public static HoldStatus Default
        {
            get { return new HoldStatus(NOT_HELD); }
        }

        # region Predefined Hold Statuses

        public static HoldStatus OnReceiving
        {
            get { return new HoldStatus(ON_TX_RECEIVING); }
        }

        public static HoldStatus OnShipping
        {
            get { return new HoldStatus(ON_TX_SHIPPING); }
        }

        public static HoldStatus OnOwnershipTransfer
        {
            get { return new HoldStatus(ON_TX_OWNERSHIP_TRANSFER); }
        }

        public static HoldStatus OnReceiptCreation
        {
            get { return new HoldStatus(ON_TX_RECEIPT_CREATE); }
        }

        public static HoldStatus OnReceiptCreationAndOwnershipTransfer
        {
            get { return new HoldStatus(ON_TX_RECEIPT_CREATE_AND_OWNERSHIP_TRANSFER); }
        }

        public static HoldStatus OnReceiptRedemption
        {
            get { return new HoldStatus(ON_TX_RECEIPT_REDEEM); }
        }

        public static HoldStatus OnReceiptRedemptionAndOwnershipTransfer
        {
            get { return new HoldStatus(ON_TX_RRECEIPT_REDEEM_AND_OWNERSHIP_TRANSFER); }
        }

        public static HoldStatus OnInterWarehouseTransfer
        {
            get { return new HoldStatus(ON_INTER_WAREHOUSE_TRANSFER); }
        }
        
        # endregion
        
        private static bool IsUserHoldStatus(int status)
        {
            return (status > 0);
        }

        private static bool IsSystemHoldStatus(int status)
        {
            return (status < 0&&status!=-11);
        }
        private static bool IsWarehouseTransferHoldStatus(int status)
        {
            return (status == -11);
        }
        private static bool IsTransactionalHoldStatus(int status)
        {
            return (status < 0 && status > -11);
        }

        # region Overrided Members

        public override bool Equals(object obj)
        {
            if (this == obj)
                return true;

            HoldStatus that = obj as HoldStatus;

            if (that == null)
                return false;

            return this.status == that.Value;
        }

        public override int GetHashCode()
        {
            return status;
        }

        # endregion

        #region IComparable Members

        public int CompareTo(object obj)
        {
            HoldStatus that = obj as HoldStatus;

            if (obj == null)
                return 1;

            if (Equals(that))
                return 0;

            return status > that.Value ? 1 : -1;
        }

        #endregion
    }
}
